package com.github.framework.context;

import com.github.framework.annotaion.Autowired;
import com.github.framework.annotaion.Controller;
import com.github.framework.annotaion.Service;
import com.github.framework.aop.JdkDynamicAopProxy;
import com.github.framework.aop.config.AopConfig;
import com.github.framework.aop.support.AdviceSupport;
import com.github.framework.beans.BeanWrapper;
import com.github.framework.beans.config.BeanDefinition;
import com.github.framework.beans.support.BeanDefinitionReader;
import javafx.application.Application;

import java.lang.reflect.Field;
import java.util.*;

/**
 * @program: springv2
 * @description: 职责:完成Bean的创建和DI
 * @author: Pop
 * @create: 2021-06-20 22:14
 **/
public class ApplicationContext {

    private BeanDefinitionReader reader;

    //缓存起来的内容
    private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<String,BeanDefinition>();
    //实例缓存
    private Map<String,BeanWrapper> factoryBeanInstanceCache = new HashMap<String,BeanWrapper>();
    //原生对象的缓存
    private Map<String,Object> factoryBeanObjectCache = new HashMap<String,Object>();

    public Properties getConfig(){
        return reader.getConfig();
    }

    /**
     * 可以多个配置的位置
     * @param configLocations
     */
    public ApplicationContext(String ...configLocations)
    {
        // 加载配置文件
        reader = new BeanDefinitionReader(configLocations);
        // 将配置信息封装成BeanDefinition
        List<BeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
        // 将BeanDefinition缓存起来(享元模式)
        doRegistBeanDefinition(beanDefinitions);
        
        //用于不延迟加载的注入
        doAutowrited();
    }

    private void doRegistBeanDefinition(List<BeanDefinition> beanDefinitions) {
        for(BeanDefinition beanDefinition:beanDefinitions)
        {
            if(this.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName()))
            {
                // 抛出异常，已经被初始化了，ioc容器不允许存在多个单例
                continue;
            }
            // 通过多个方式注入
            this.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(),beanDefinition);
            this.beanDefinitionMap.put(beanDefinition.getBeanClassName(),beanDefinition);
        }
    }

    private void doAutowrited() {
        // getBean() 会注入
        //调用getBean()
        //这一步，所有的Bean并没有真正的实例化，还只是配置阶段
        for (Map.Entry<String,BeanDefinition> beanDefinitionEntry : this.beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();
            getBean(beanName);
        }
    }

    /**
     * Bean的实例化，DI也是从这个方法开始
     * @param beanName
     * @return
     */
    public Object getBean(String beanName)
    {
        //首先拿到配置信息
        BeanDefinition beanDefinition=this.beanDefinitionMap.get(beanName);
        // 进行实例化
        Object instance = instantiateBean(beanName,beanDefinition);
        // 封装成BeanWrapper 用于后续的扩展和增强 装饰器模式
        BeanWrapper beanWrapper = new BeanWrapper(instance);
        // 保存到IOC容器
        factoryBeanInstanceCache.put(beanName,beanWrapper);
        // 依赖注入
        populateBean(beanName,beanDefinition,beanWrapper);
        //将循环依赖的对象再尝试初始化一下
//        loopDIHandler();
        return  factoryBeanInstanceCache.get(beanName).getWrapperInstance();
    }


    /**
     * DI
     * @param beanName
     * @param beanDefinition
     * @param beanWrapper
     */
    private void populateBean(String beanName, BeanDefinition beanDefinition, BeanWrapper beanWrapper) {
        // 可能涉及到循环依赖
        // 将第一次找不到的BeanDefinition去第二次循环赋值
        // 扫描字段
        Class<?> clazz = beanWrapper.getWrapperClass();
        Object instance = beanWrapper.getWrapperInstance();
        //是@Compent的派生类我们才选择注入
        if(!(clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(Service.class))){
            return;
        }
        Field[] fields = clazz.getDeclaredFields();
        for(Field field:fields)
        {
            // 不含有就不作为
            if(!field.isAnnotationPresent(Autowired.class)){continue;}
            Autowired autowired=field.getAnnotation(Autowired.class);

            //应该字段应该是个接口，不该是实现类
            if(!field.getType().isInterface()){continue;}

            String autowritedBeanName = autowired.value().trim();
            if("".equals(autowritedBeanName))
            {
                //没有自定义的名字，就直接按照类型注入
                autowritedBeanName = field.getType().getName();
            }

            Object autowritedObject = null;
            if(!factoryBeanInstanceCache.containsKey(autowritedBeanName))
            {
                //找不到依赖的对象
                //也可能是循环依赖，被依赖的对象还没有被初始化
                //先放入缓存
                // 这里另外一种解决循环依赖的想法，使用递归，将所有的需要被注入的内容初始化
                autowritedObject=getBean(autowritedBeanName);
                continue;
            }
            autowritedObject = factoryBeanInstanceCache.get(autowritedBeanName).getWrapperInstance();
            try {
                field.setAccessible(true);
                // 一定要从容器中取出

                field.set(instance,autowritedObject);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 创建最纯粹的对象
     * @param beanName
     * @param beanDefinition
     * @return
     */
    private Object instantiateBean(String beanName,BeanDefinition beanDefinition) {
        String className = beanDefinition.getBeanClassName();
        Object instance = null;
        try {
            Class<?> clazz = Class.forName(className);
            // 保证单例
            if(factoryBeanObjectCache.containsKey(beanName))
            {
                return factoryBeanObjectCache.get(beanName);
            }
            instance  = clazz.newInstance();

            // Aop的开始
            //==================AOP开始=========================
            //如果满足条件，就直接返回Proxy对象
            //1、加载AOP的配置文件

            AdviceSupport config = instantionAopConfig();
            config.setTargetClass(clazz);
            config.setTarget(instance);

            //判断规则，要不要生成代理类，如果要就覆盖原生对象
            //如果不要就不做任何处理，返回原生对象
            if(config.pointCutMath()){
                instance = new JdkDynamicAopProxy(config).getProxy();
            }

            //===================AOP结束========================


            // 保存原生对象 类似备份
            factoryBeanObjectCache.put(beanName,instance);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }

    // 生成配置文件
    private AdviceSupport instantionAopConfig() {
        AopConfig config = new AopConfig();
        config.setPointCut(this.reader.getConfig().getProperty("pointCut"));
        config.setAspectClass(this.reader.getConfig().getProperty("aspectClass"));
        config.setAspectBefore(this.reader.getConfig().getProperty("aspectBefore"));
        config.setAspectAfter(this.reader.getConfig().getProperty("aspectAfter"));
        config.setAspectAfterThrow(this.reader.getConfig().getProperty("aspectAfterThrow"));
        config.setAspectAfterThrowingName(this.reader.getConfig().getProperty("aspectAfterThrowingName"));
        return new AdviceSupport(config);
    }

    public Object getBean(Class beanClass)
    {
        return getBean(beanClass.getName());
    }

    /**
     * 原Spring中也有，表示读取到的配置的个数
     * @return
     */
    public int getBeanDefinitionCount() {
        return this.beanDefinitionMap.size();
    }

    /**
     * 获得容器中已经初始化好的实例名称列表
     * @return
     */
    public String[] getBeanDefinitionNames() {
        return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
    }
}
